import * as TOOL from "../tool"
import { DataSegment, SegmentItem } from "../../datastructure/DataSegment"
export class MapBar {
    constructor(element?: Element | string | null | undefined) {
        this.element = TOOL.creatElement(element);
        this.canvas = document.createElement("canvas");
        this.toolBar = document.createElement("div");
        let tmp = this.canvas.getContext("2d");
        if (!tmp) {
            alert("你的浏览器不支持 canvas");
        }
        this.canvasContext = tmp!;
        this.element.style.overflow = "hidden"
        this.element.appendChild(this.canvas);
        this.element.appendChild(this.toolBar);

        this.blockSize = 50;
        this.mapHeight = 10;
        this.mapWidth = 10;
        this.initSize(this.mapWidth, this.mapHeight, this.blockSize);

        //TEST IMG
        this.img.src = 'http://pic.66rpg.com/ftp_pic/pic872.jpg';

        //TEST MATRIX
        this.matrix = this.makeMatrix(this.mapWidth, this.mapHeight);
        this.matrix = new Array<string>();

        this.buttons = this.buttonInit();
        for (let btn of this.buttons) {
            this.toolBar.appendChild(btn);
        }

        this.img.onload = this.drawImg.bind(this);
        this.img.onerror = this.errorImg.bind(this);
        this.toolBar.onmouseover = this.onToolBarMouseOver.bind(this);
        this.toolBar.onmouseout = this.onToolBarMouseOut.bind(this);
        this.canvas.onclick = this.mouseClick.bind(this);
        this.element.onmouseover = this.onMouseOver.bind(this);
        this.element.onmouseout = this.onMouseOut.bind(this);
        this.element.onmousedown = this.onMouseDown.bind(this);
        this.element.onmouseup = this.onMouseUp.bind(this);
        this.element.onmousemove = this.onMouseMove.bind(this);
        this.element.onmousewheel = this.onMouseWheel.bind(this);
        document.onkeypress = this.onKeyDown.bind(this);

        this.initLine();
        this.toolBarInit();
    }

    public initSize(mapWidth: number, mapHeight: number, blockSize?: number) {
        this.mapWidth = mapWidth;
        this.mapHeight = mapHeight;
        if (blockSize) {
            this.blockSize = blockSize;
        }
        this.canvas.width = this.mapWidth * this.blockSize;
        this.canvas.height = this.mapHeight * this.blockSize;
        this.canvasWidth = this.canvas.width;
        this.canvasHeight = this.canvas.height;
    }

    public initLine() {
        this.initSize(this.mapWidth, this.mapHeight, this.blockSize);
        this.canvasContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
        if (this.imgExist) {
            this.drawImg(this.img);
        }
        this.canvasContext.strokeStyle = "rgba(128,128,128)";
        for (let i = 0; i <= this.canvasWidth; i += this.blockSize) {
            this.canvasContext.moveTo(i, 0);
            this.canvasContext.lineTo(i, this.canvasHeight);
        }
        for (let i = 0; i <= this.canvasHeight; i += this.blockSize) {
            this.canvasContext.moveTo(0, i);
            this.canvasContext.lineTo(this.canvasWidth, i);
        }
        //alert(`width: ${this.canvasWidth},height: ${this.canvasHeight}`);
        /*
        for (let i = 0; i < this.canvasWidth; i += this.blockSize) {
            for (let j = 0; j < this.canvasHeight; j += this.blockSize) {
                let x = (i / this.blockSize);
                let y = (j / this.blockSize);
                this.canvasContext.strokeText(
                    "(" + x.toString() + "," + y.toString() + ")"
                    , i + 15, j + 30);
            }
        }
        */
        this.canvasContext.stroke();

    }

    public toolBarInit() {
        this.toolBar.style.position = "absolute";
        this.toolBar.style.backgroundColor = "rgba(0,0,0,0.3)";
        this.toolBar.style.height = "400px";
        this.toolBar.style.width = "40px";
        this.toolBar.style.top = "0px";
        this.toolBar.style.left = "0px";
    }

    public buttonInit(): HTMLButtonElement[] {
        let btns: HTMLButtonElement[] = new Array<HTMLButtonElement>();
        //Init buttons      
        for (let i = 0; i < 10; i++) {
            btns[i] = document.createElement("button");
            btns[i].type = "button";
            btns[i].className = "btn btn-light";
            btns[i].innerText = `btn${i}`;
            btns[i].style.userSelect = "none";
            btns[i].onclick = function () {
                alert(`button${i} clicked!`);
            };
        }
        //Customize buttons
        //HELP button
        btns[0].innerText = "HELP";
        btns[0].className = "btn btn-info";
        btns[0].onclick = function () {
            alert(`W A S D: Move Map\nQ E: Scale Map\nR: Reset Map Pos and Scale\nT: Show/Hide Tools\n`);
        };
        //Clear Button
        btns[1].innerText = "Generate";
        btns[1].className = "btn btn-warning";
        btns[1].onclick = this.devConsole.bind(this, "clearmap");
        //Background Fill button
        btns[2].innerText = "BgFill";
        btns[2].onclick = this.devConsole.bind(this, "fillmap");
        //Resplit Button
        btns[3].innerText = "Resplit";
        btns[3].onclick = this.devConsole.bind(this, "resplit");
        //Show Matrix Button
        btns[4].innerHTML = "Export M";
        btns[4].onclick = this.exportMatrix.bind(this);
        //Set Matrix Button
        btns[5].innerHTML = "Import M";
        btns[5].onclick = this.importMatrix.bind(this);
        //Set Img Button
        btns[6].innerHTML = "Set Img";
        btns[6].onclick = this.devConsole.bind(this, "setimg");
        //Draw Button
        btns[7].innerHTML = "Draw";
        btns[7].className = "btn btn-success";
        btns[7].onclick = this.drawSwitch.bind(this);
        //Dev Button
        btns[8].innerHTML = "";
        btns[8].className = "btn btn-light";
        btns[8].onclick = this.devSwitch.bind(this);
        //Create Button
        btns[9].innerHTML = "CreateObj";
        btns[9].className = "btn btn-success";
        btns[9].onclick = this.createSwitch.bind(this);
        //Other Buttons
        return btns;
    }

    //Matrix Generator
    public makeMatrix(x: number, y?: number): Array<string> {
        let mat;
        if (y) {
            mat = new Array<string>();
            for (let i = 0; i < x * y; i++) {
                mat[i] = String(Math.floor(Math.random() * 100) % 10);
            }
        } else {
            mat = new Array<string>();
            for (let i = 0; i < x * x; i++) {
                mat[i] = String(Math.floor(Math.random() * 100) % 10);
            }
        }
        return mat;
    }

    public clearMap() {
        this.initLine();
        this.onMessageSend("clearmap", "hotupdate");
    }

    public bgFill() {
        this.initLine();
        this.drawObj();
        this.onMessageSend("fillmap", "hotupdate");
    }

    public resplitMap() {
        let x: number = Number(prompt("resplit x: ", String(this.mapWidth)));
        let y: number = Number(prompt("resplit y: ", String(this.mapHeight)));
        if (this.devMode) {
            let b: number = Number(prompt("blockSize: ", String(this.blockSize)));
            this.initSize(x, y, b);
        } else {
            this.initSize(x, y);
        }
        this.initLine();
        this.onMessageSend(`set_split ${this.mapWidth},${this.mapHeight},${this.blockSize}`, "hotupdate");
    }

    public exportMatrix() {
        prompt("export matrix", String(this.matrix));
    }

    public importMatrix(mx?: string) {
        let k: number = 0;
        let mtx: string[];
        if (mx && typeof mx == "string") {
            mtx = mx.split(",");
        } else {
            mtx = prompt("Import Matrix", "")!.split(",");
        }
        for (let m of mtx) {
            this.matrix[k] = m;
            k++;
        }
        this.drawObj();
        this.onMessageSend(`set_img ${this.img.src}`, "hotupdate");
    }

    public setImg(src?: string) {
        if (src && typeof src == "string") {
            this.img.src = src;
        } else {
            this.img.src = String(prompt("Input Img Url: ", this.img.src));
        }
        this.initLine();
        this.onMessageSend(`set_img ${this.img.src}`, "hotupdate");
    }

    public drawSwitch() {
        if (this.drawOpen) {
            this.drawOff();
        } else {
            this.drawOn();
            this.createOff();
        }
    }

    public drawOn() {
        this.drawOpen = true;
        this.buttons[7].innerText = "Stop";
        this.buttons[7].className = "btn btn-danger";
    }

    public drawOff() {
        this.drawOpen = false;
        this.buttons[7].innerText = "Draw";
        this.buttons[7].className = "btn btn-success";
    }

    public createSwitch() {
        if (this.createOpen) {
            this.createOff();
        } else {
            this.createOn();
            this.drawOff();
        }
    }

    public createOn() {
        this.createOpen = true;
        this.createId = prompt("Create number: ", "") as string;
        this.buttons[9].innerText = "CreateObj";
        this.buttons[9].className = "btn btn-danger";
    }

    public createOff() {
        this.createOpen = false;
        this.buttons[9].innerText = "CreateObj";
        this.buttons[9].className = "btn btn-success";
    }

    public devSwitch() {
        if (this.devMode) {
            this.devMode = false;
            alert("DevMode OFF");
        } else {
            this.devMode = true;
            alert("DevMode ON\nPress C to Open Console.\nYou can also change blockSize in resplit.\n\nAvailable command:\nset_matrix\nset_img\nset_trans\nget_matrix\nget_img\nget_trans\nget_split\nset_split\nclearmap\nfillmap\ndrawmouse\nsave\ntest\nresplit\nsetimg\nchangeMatrix\n");
            this.devConsole();
        }
    }

    public static CONSOLE_COMMAND = new Set([
        "get_matrix", "set_matrix", "get_img", "set_img", "get_trans", "set_trans", "get_split", "set_split",
        "clearmap", "fillmap", "drawmouse", "resplit", "setimg", "changeMatrix", "test"
    ]);
    public devConsole(co?: string) {
        let con: string;
        if (co && typeof co == "string") {
            con = co;
        } else {
            con = String(prompt("console", ""));
        }
        if (con != "" && con != "null") {
            let preCmd = con.split(";");
            for (let cmd of preCmd) {
                let command: string[] = cmd.split(" ");
                //Command Excution
                //alert(`Excuted:\nfunction->${command[0]}\narg->${command[1]}`);
                switch (command[0]) {
                    case "get_matrix":
                        prompt("get_matrix", `set_matrix ${this.matrix}`);
                        break;
                    case "set_matrix":
                        let mx: string = command[1];
                        let k: number = 0;
                        let mtx: string[];
                        if (mx && typeof mx == "string") {
                            mtx = mx.split(",");
                        } else {
                            mtx = prompt("Import Matrix", "")!.split(",");
                        }
                        for (let m of mtx) {
                            this.matrix[k] = m;
                            k++;
                        }
                        this.drawObj();
                        break;
                    case "get_img":
                        prompt("get_img", `set_img ${this.img.src}`);
                        break;
                    case "set_img":
                        let src: string = command[1];
                        if (src && typeof src == "string") {
                            this.img.src = src;
                        } else {
                            this.img.src = String(prompt("Input Img Url: ", this.img.src));
                        }
                        this.initLine();
                        break;
                    case "get_trans":
                        prompt("get_trans", `set_trans ${this.moveX},${this.moveY},${this.scale}`);
                        break;
                    case "set_trans":
                        let x: number = Number(command[1].split(",")[0]);
                        let y: number = Number(command[1].split(",")[1]);
                        let s: number = Number(command[1].split(",")[2]);
                        this.scale = s;
                        this.moveCameraTo(x, y);
                        break;
                    case "get_split":
                        prompt("get_split", `set_split ${this.mapWidth},${this.mapHeight},${this.blockSize}`);
                        break;
                    case "set_split":
                        let width: number = Number(command[1].split(",")[0]);
                        let height: number = Number(command[1].split(",")[1]);
                        let blockSize: number = Number(command[1].split(",")[2]);
                        this.initSize(width, height, blockSize);
                        this.initLine();
                        break;
                    case "clearmap":
                        this.initLine();
                        break;
                    case "fillmap":
                        this.initLine();
                        this.drawObj();
                        break;
                    case "drawmouse":
                        this.drawMouse(command[1]);
                        break;
                    case "save":
                        prompt("save content: ", `set_matrix ${this.matrix};set_img ${this.img.src};set_split ${this.mapWidth},${this.mapHeight},${this.blockSize};set_trans ${this.moveX},${this.moveY},${this.scale};clearmap;fillmap;`);
                        this.onMessageSend(`set_matrix ${this.matrix};set_img ${this.img.src};set_split ${this.mapWidth},${this.mapHeight},${this.blockSize};set_trans ${this.moveX},${this.moveY},${this.scale};clearmap;fillmap;`, "save");
                        break;
                    case "resplit":
                        this.resplitMap();
                        break;
                    case "setimg":
                        this.setImg();
                        break;
                    case "changeMatrix":
                        let count: number = Number(command[1].split(",")[0]);
                        let num: string = command[1].split(",")[1];
                        this.matrix[count] = num;
                        this.devConsole("fillmap");
                        break;
                    case "test":
                        prompt("test", "string".split(";")[0]);
                        break;
                }
            }
        }
    }

    public drawMouse(track: string) {
        let preCvt: string[] = track.split(",");
        let tk: number[] = new Array<number>();
        for (let cvt of preCvt) {
            tk.push(Number(cvt));
        }
        this.canvasContext.moveTo(tk[0], tk[1]);
        for (let i = 2; i < tk.length; i += 2) {
            this.canvasContext.lineTo(tk[i], tk[i + 1]);
            this.canvasContext.stroke();
        }
    }

    public drawObj() {
        let count: number = 0;
        for (let i = 0; i < this.canvasHeight; i += this.blockSize) {
            for (let j = 0; j < this.canvasWidth; j += this.blockSize) {
                switch (this.matrix[count]) {
                    case "1":
                        this.canvasContext.fillStyle = "rgb(255,0,0)";
                        break;
                    case "2":
                        this.canvasContext.fillStyle = "rgb(0,255,0)";
                        break;
                    case "3":
                        this.canvasContext.fillStyle = "rgb(0,0,255)";
                        break;
                    case "4":
                        this.canvasContext.fillStyle = "rgb(255,255,0)";
                        break;
                    case "5":
                        this.canvasContext.fillStyle = "rgb(0,255,255)";
                        break;
                    case "red":
                        this.drawCircle(j, i, "red");
                        break;
                    case "blue":
                        this.drawCircle(j, i, "blue");
                        break;
                    case "orange":
                        this.drawCircle(j, i, "orange");
                        break;
                    case "green":
                        this.drawCircle(j, i, "green");
                        break;
                    default:
                        this.canvasContext.fillStyle = "rgb(0,0,0)";
                }
                //this.canvasContext.fillRect(j, i, j + this.blockSize, i + this.blockSize);
                count++;
            }
        }
    }

    public drawCircle(x: number, y: number, color: string) {
        this.canvasContext.fillStyle = color;
        this.canvasContext.beginPath();
        this.canvasContext.arc(x + this.blockSize / 2, y + this.blockSize / 2, this.blockSize / 3, 0, Math.PI * 2, true);
        this.canvasContext.closePath();
        this.canvasContext.fill();
    }

    public drawImg(img: HTMLImageElement) {
        this.imgExist = true;
        this.canvasContext.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight);
    }
    public errorImg() {
        this.imgExist = false;
    }

    public moveCameraTo(x: number, y: number) {
        this.moveX = x;
        this.moveY = y;
        this._render()
    }
    public moveCamera(x: number, y: number) {
        this.moveX += x / this.scale;
        this.moveY += y / this.scale;
        this._render();
        this.onMessageSend(`set_trans ${this.moveX},${this.moveY},${this.scale}`, "hotupdate");
    }
    public scaleCamera(time: number) {
        this.rescale(time);
        this._render();
        this.onMessageSend(`set_trans ${this.moveX},${this.moveY},${this.scale}`, "hotupdate");
    }
    public resize(height: number, width: number) {
        this.element!.style.height = height * 0.8 + "px";
        this.element!.style.width = width * 0.8 + "px";
        this.element!.style.top = "0";
        this.element!.style.left = "0";
    }
    public rescale(time: number) {
        if (time == 1) {//zoom out
            if (this.scale > 0.07) {
                this.scale -= 0.05;
            }
        }
        if (time == 0) {//zoom in
            if (this.scale < 2) {
                this.scale += 0.05;
            }
        }
    }
    public resetToOrigin() {
        this.moveX = this.OriginX;
        this.moveY = this.OriginY;
    }
    public resetMap() {
        this.scale = 1;
        this.canvasWidth = this.canvas.width;
        this.canvasHeight = this.canvas.height;
        this.resetToOrigin();
        this._render();
    }
    private _render() {
        this.canvas!.style.transformOrigin = "top left";
        this.canvas!.style.transform = `scale(${this.scale, this.scale}) translate(${this.moveX}px,${this.moveY}px)`;
    }
    private onMouseMove(ev: MouseEvent) {
        if (this.isMouseDown && !this.drawOpen) {
            this.moveCamera(ev.movementX, ev.movementY);
        }
        if (ev.preventDefault) {
            ev.preventDefault();
        }
        //draw
        if (this.isMouseDown && this.drawOpen && !this.isMouseOverTool) {
            this.lastX = ev.offsetX;
            this.lastY = ev.offsetY;
            this.canvasContext.lineTo(this.lastX, this.lastY);
            this.mouseDrawMap.push(String(this.lastX));
            this.mouseDrawMap.push(String(this.lastY));
            this.canvasContext.stroke();
        }
    }
    private onMouseDown(ev: MouseEvent) {
        if (this.isMouseOver && !this.isMouseOverTool) {
            this.isMouseDown = true;
        }
        //draw
        if (ev.button == 0 && this.drawOpen) {
            this.orignalX = ev.offsetX;
            this.orignalY = ev.offsetY;
            this.canvasContext.moveTo(this.orignalX, this.orignalY);
            this.mouseDrawMap.push(String(this.orignalX));
            this.mouseDrawMap.push(String(this.orignalY));
            this.canvasContext.beginPath();
            this.isMouseDown = true;

        }
    }
    private onMouseUp(ev: MouseEvent) {
        //draw
        if (this.isMouseDown && this.drawOpen && !this.isMouseOverTool) {
            this.lastX = ev.offsetX;
            this.lastY = ev.offsetY;
            this.canvasContext.lineTo(this.lastX, this.lastY);
            this.mouseDrawMap.push(String(this.lastX));
            this.mouseDrawMap.push(String(this.lastY));
            //prompt("drawmouse", `drawmouse ${String(this.mouseDrawMap)}`);
            this.onMessageSend(`drawmouse ${String(this.mouseDrawMap)}`, "hotupdate");
            this.canvasContext.stroke();
            this.lastX = 0;
            this.lastY = 0;
            this.orignalX = 0;
            this.orignalY = 0;
            this.mouseDrawMap = new Array<string>();
            this.canvasContext.closePath();
        }
        //
        this.isMouseDown = false;
    }
    private onMouseOver(ev: MouseEvent) {
        if (!this.isMouseOverTool) {
            this.isMouseOver = true;
        }
    }
    private onMouseOut(ev: MouseEvent) {
        this.isMouseOver = false;
        this.isMouseDown = false;
    }
    private onToolBarMouseOver(ev: MouseEvent) {
        this.isMouseOverTool = true;
    }
    private onToolBarMouseOut(ev: MouseEvent) {
        this.isMouseOverTool = false;
    }
    private onMouseWheel(ev: any) {
        let e: any = ev || window.event;
        let down: boolean = true;
        down = e.wheelDelta ? e.wheelDelta < 0 : e.detail > 0;
        if (down) {
            this.scaleCamera(1);
        } else {
            this.scaleCamera(0);
        }
        if (ev.preventDefault) {
            ev.preventDefault();
        }
        return false;
    }
    private onKeyDown(ev: KeyboardEvent) {
        if ((this.isMouseOver || this.isMouseOverTool)) {
            if (ev.code == "KeyT") {
                if (this.toolBar.style.visibility == "hidden") {
                    this.toolBar.style.visibility = "visible";
                } else {
                    this.toolBar.style.visibility = "hidden";
                }
            } else if (ev.code == "KeyC" && this.devMode) {
                this.devConsole();
            }
        }
        if (this.isMouseOver && !this.isMouseOverTool) {
            switch (ev.code) {
                case "KeyS":
                case "ArrowDown":
                    this.moveCamera(0, this.blockSize);
                    break;
                case "KeyW":
                case "ArrowUp":
                    this.moveCamera(0, -this.blockSize);
                    break;
                case "KeyA":
                case "ArrowLeft":
                    this.moveCamera(-this.blockSize, 0);
                    break;
                case "KeyD":
                case "ArrowRight":
                    this.moveCamera(this.blockSize, 0);
                    break;
                case "KeyQ":
                    this.scaleCamera(1);
                    break;
                case "KeyE":
                    this.scaleCamera(0);
                    break;
                case "KeyR":
                    this.resetMap();
                    break;
            }
        }
    }

    public mouseClick(ev: MouseEvent) {
        var rect = this.canvas.getBoundingClientRect();
        let x: number = ev.clientX - rect.left;
        let y: number = ev.clientY - rect.top;
        let xPos: number = 0;
        let yPos: number = 0;
        let count: number = 0;
        xPos = Math.floor(x / (this.blockSize * this.scale));
        yPos = Math.floor(y / (this.blockSize * this.scale));
        count = yPos * this.mapWidth + xPos;
        if (this.createOpen) {
            if (this.matrix[count] == this.createId) {
                this.matrix[count] = "0";
                this.onMessageSend(`changeMatrix ${count},0`, "hotupdate");
            } else {
                this.matrix[count] = this.createId;
                this.onMessageSend(`changeMatrix ${count},${this.createId}`, "hotupdate");
            }
            this.bgFill();
        }
        //alert(`${x} , ${y} \n${xPos} , ${yPos}\n${count}`);
    }

    public onMap(message: DataSegment) {
        if (message.get("what") as string == "save") {
            this.devConsole("save");
        } else {
            this.devConsole(message.get("data") as string);
        }
    }

    public onMessageSend = (str: string, what: string) => {
    };

    private blockSize: number;
    private mapHeight: number;
    private mapWidth: number;
    private isMouseDown: boolean = false;
    private isMouseOver: boolean = false;
    private isMouseOverTool: boolean = false;
    private moveX: number = 0;
    private moveY: number = 0;
    private canvas: HTMLCanvasElement;
    private toolBar: HTMLElement;
    private canvasWidth: number = 0;
    private canvasHeight: number = 0;
    public element: HTMLElement | null;
    private canvasContext: CanvasRenderingContext2D;
    private scale: number = 1;
    private OriginX: number = 0;
    private OriginY: number = 0;
    private buttons: HTMLButtonElement[] = new Array<HTMLButtonElement>();
    private matrix: string[];
    private img: HTMLImageElement = new Image();
    private orignalX: number = 0;
    private orignalY: number = 0;
    private lastX: number = 0;
    private lastY: number = 0;
    private drawOpen: boolean = false;
    private devMode: boolean = false;
    private imgExist: boolean = false;
    private createOpen: boolean = false;
    private createId: string = "0";
    private mouseDrawMap: string[] = new Array<string>();
}